/**
 * 版权所有(C)，上海勾芒信息科技，2017，所有权利保留。
 * 
 * 项目名：	cards-web
 * 文件名：	WxAppConfigStorageImpl.java
 * 模块说明：	
 * 修改历史：
 * 2017年4月13日 - Debenson - 创建。
 */
package com.alex.wechat.component.config;

import java.net.URL;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;

import com.alex.insistence.web.filter.InitWapContextFilter;
import com.alex.wechat.component.account.entity.WxComponentAccountEntity;
import com.alex.wechat.component.account.service.WxComponentAccountServiceI;

import me.chanjar.weixin.component.api.WxCompConfigStorage;
import me.chanjar.weixin.component.bean.WxComponentAccessToken;
import me.chanjar.weixin.component.bean.author.WxAuthorizerAccessToken;
import me.chanjar.weixin.component.bean.author.WxPreauthorCode;

/**
 * 微信开放平台账户配置存储(基于数据库的实现)
 * 
 * @author Debenson
 * @since 0.1
 */
public class WxCompDbConfigStorage implements WxCompConfigStorage {

  @Autowired
  private WxComponentAccountServiceI compAccountService;
  @Autowired
  private AuthorizerAccessTokenStorageFactory authorizerStorageFactory;

  /**
   * 微信通用TOKEN
   */
  @Value("${wx.token}")
  private String token;
  @Value("${domain}")
  private String webBaseUrl;

  private final Lock accessTokenLock = new ReentrantLock();
  private final Lock preauthorLock = new ReentrantLock();
  private final Lock authorizerAccessTokenLock = new ReentrantLock();

  protected WxComponentAccountEntity ensureAccount() {
    WxComponentAccountEntity account = WxComponentContext.getComponentAccount();
    if (account == null) {
      throw new RuntimeException("没有配置当前第三方开放平台账户");
    }
    return account;
  }

  @Override
  public String getAppId() {
    return ensureAccount().getAppId();
  }

  @Override
  public String getSecret() {
    return ensureAccount().getAppSecret();
  }

  @Override
  public String getToken() {
    return token;
  }

  @Override
  public String getAesKey() {
    return ensureAccount().getAesKey();
  }

  @Override
  public String getVerifyTicket() {
    return ensureAccount().getVerifyTicket();
  }

  @Override
  public String getAccessToken() {
    return ensureAccount().getAccessToken();
  }

  @Override
  public Lock getAccessTokenLock() {
    return accessTokenLock;
  }

  @Override
  public boolean isAccessTokenExpired() {
    WxComponentAccountEntity account = ensureAccount();
    long expiresTime = (account.getAccessTokenExpiresIn() == null
        || StringUtils.isBlank(account.getAccessToken())) ? 0 : account.getAccessTokenExpiresIn();
    return System.currentTimeMillis() > expiresTime;
  }

  @Override
  public void expireAccessToken() {
    WxComponentAccountEntity account = ensureAccount();
    account.setAccessTokenExpiresIn(0L);
    try {
      compAccountService.saveOrUpdate(account);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public void updateAccessToken(WxComponentAccessToken accessToken) {
    updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
  }

  @Override
  public void updateAccessToken(String accessToken, int expiresInSeconds) {
    WxComponentAccountEntity account = ensureAccount();
    account.setAccessToken(accessToken);
    long expiresIn = 0L;
    if (StringUtils.isNotBlank(accessToken)) {
      expiresIn = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000L;
    }
    account.setAccessTokenExpiresIn(expiresIn);

    try {
      compAccountService.saveOrUpdate(account);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public String getDomain() {
    try {
      URL url = new URL(webBaseUrl);
      return url.getHost();
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public String getAuthorizationCallbackUrl() {
    WxComponentAccountEntity account = ensureAccount();
    return webBaseUrl + "/wxCompAuthController/authorCallback.do?"
        + InitWapContextFilter.PARAM_WX_COMPONENT_APP_ID + "=" + account.getAppId();
  }

  @Override
  public String getPreauthorizationCode() {
    return ensureAccount().getPreAuthCode();
  }

  @Override
  public Lock getPreauthorCodeLock() {
    return preauthorLock;
  }

  @Override
  public boolean isPreauthorCodeExpired() {
    WxComponentAccountEntity account = ensureAccount();
    long expiresTime = (account.getPreAuthCodeExpiresIn() == null
        || StringUtils.isBlank(account.getPreAuthCode())) ? 0 : account.getPreAuthCodeExpiresIn();
    return System.currentTimeMillis() > expiresTime;
  }

  @Override
  public void expirePreauthorCode() {
    WxComponentAccountEntity account = ensureAccount();
    account.setPreAuthCodeExpiresIn(0L);
    try {
      compAccountService.saveOrUpdate(account);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public void updatePreauthorCode(WxPreauthorCode preauthorCode) {
    updatePreauthorCode(preauthorCode.getCode(), preauthorCode.getExpiresIn());
  }

  @Override
  public void updatePreauthorCode(String preauthorCode, int expiresInSeconds) {
    WxComponentAccountEntity account = ensureAccount();
    account.setPreAuthCode(preauthorCode);
    if (StringUtils.isBlank(preauthorCode)) {
      account.setPreAuthCodeExpiresIn(0L);
    } else {
      account
          .setPreAuthCodeExpiresIn(System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l);
    }
    try {
      compAccountService.saveOrUpdate(account);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  @Override
  public Lock getAuthorizerAccessTokenLock() {
    return authorizerAccessTokenLock;
  }

  @Override
  public boolean isAuthorizerAccessTokenExpired(String appId) {
    return authorizerStorageFactory.getStorage(appId).isAuthorizerAccessTokenExpired();
  }

  @Override
  public void expireAuthorizerAccessToken(String appId) {
    authorizerStorageFactory.getStorage(appId).expireAuthorizerAccessToken();
  }

  @Override
  public void updateAuthorizerAccessToken(String appId, String authorizationCode,
      int expiresInSeconds) {
    authorizerStorageFactory.getStorage(appId).updateAuthorizerAccessToken(authorizationCode,
        expiresInSeconds);
  }

  @Override
  public String getAuthorizerAccessToken(String appId) {
    return authorizerStorageFactory.getStorage(appId).getAuthorizerAccessToken();
  }

  @Override
  public void updateAuthorizerAccessToken(String appId, String token, int expiresInSeconds,
      String authorizerRefreshToken) {
    authorizerStorageFactory.getStorage(appId).updateAuthorizerAccessToken(token, expiresInSeconds,
        authorizerRefreshToken);
  }

  @Override
  public String getAuthorizerRefreshToken(String appId) {
    return authorizerStorageFactory.getStorage(appId).getAuthorizerRefreshToken();
  }

  @Override
  public void updateAuthorizerAccessToken(String appId, WxAuthorizerAccessToken token) {
    authorizerStorageFactory.getStorage(appId).updateAuthorizerAccessToken(
        token.getAuthorizerAccessToken(), token.getExpiresIn(), token.getAuthorizerRefreshToken());
  }

}
